import 'dart:developer' as developer;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';

import '../../aegis_flutter_sdk.dart';
import '../model/options.dart';
import 'page_info.dart';
import 'page_info_util.dart';
import 'page_render_object.dart';

const int _invalidIndex = -1;

/// 页面覆盖率阀值，用于统计页面打开速度
const double _verticalCoverFactor = 1.0;
const double _horizontalCoverFactor = 0.0;

enum NavigatorType {
  push,
  pop,
  remove,
  replace,
}

/// 页面切换时计算页面加载耗时
class PageNavigatorObserver extends NavigatorObserver {
  factory PageNavigatorObserver() => _getInstance();
  PageNavigatorObserver._internal();
  static PageNavigatorObserver get instance => _getInstance();
  static PageNavigatorObserver? _instance;
  static PageNavigatorObserver _getInstance() {
    _instance ??= PageNavigatorObserver._internal();

    return _instance!;
  }

  /// 上报类
  Aegis? _aegis;

  /// 记录页面信息
  final List<PageInfo> _pageStack = <PageInfo>[];

  /// 当前页面信息
  PageInfo? _currentPageInfo;

  /// 当前 route
  Route? curRoute;

  /// 时间记录
  int _startTime = 0;
  int _updateTime = 0;
  int _endTime = 0;

  /// 预留给外部调用
  int curPageLoadTime = 0;
  PageStatus? _pageStatus;
  late Rect _curLoadArea;
  bool _isLoaded = false;
  PageInfo? _loadingPage;
  bool _hasReport = false;

  RenderView? _rootView;

  void init(Aegis aegis) {
    _aegis = aegis;

    if (!aegis.config.enablePageLoadTime) {
      return;
    }

    // 监听帧绘制结束的事件
    SchedulerBinding.instance?.addPostFrameCallback(_onPostFrameCallback);
  }

  @override
  void didPush(Route route, Route? previousRoute) {
    developer.log(
      '[PageNavigatorObserver][didPush]route: ${PageInfo.convertPageName(route)}, pre: ${PageInfo.convertPageName(previousRoute)}',
      name: debugLogType.pagePerformance,
    );

    _reportPV(PageInfo.convertPageName(route));
    _pageStack.add(PageInfo.route(route));
    _handleNavigator(NavigatorType.push);
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    developer.log(
      '[PageNavigatorObserver][didPop]route: ${PageInfo.convertPageName(route)}, pre: ${PageInfo.convertPageName(previousRoute)}',
      name: debugLogType.pagePerformance,
    );
    if (_pageStack.isNotEmpty) {
      _pageStack.removeLast();
      _handleNavigator(NavigatorType.pop);
    }
  }

  @override
  void didReplace({Route? newRoute, Route? oldRoute}) {
    developer.log(
      '[PageNavigatorObserver][didReplace]newRoute: ${PageInfo.convertPageName(newRoute)}, oldRoute: ${PageInfo.convertPageName(oldRoute)}',
      name: debugLogType.pagePerformance,
    );
    if (newRoute == null) {
      return;
    }

    int index = _invalidIndex;
    if (oldRoute != null) {
      index = _pageStack
          .indexWhere((pageInfo) => pageInfo.uniqueId == oldRoute.hashCode);
    }

    if (index > _invalidIndex) {
      _pageStack[index] = PageInfo.route(newRoute);
    } else {
      _pageStack.add(PageInfo.route(newRoute));
    }

    _handleNavigator(NavigatorType.replace);
  }

  @override
  void didRemove(Route route, Route? previousRoute) {
    developer.log(
        '[PageNavigatorObserver][didReplace]route: ${PageInfo.convertPageName(route)}, pre: ${PageInfo.convertPageName(previousRoute)}',
        name: debugLogType.pagePerformance);
    int index = _pageStack
        .indexWhere((pageInfo) => pageInfo.uniqueId == route.hashCode);
    if (index < 0 || index >= _pageStack.length) {
      return;
    }
    _pageStack.removeAt(index);
    _handleNavigator(NavigatorType.remove);
  }

  void _handleNavigator(NavigatorType type) {
    if (_aegis == null || !_aegis!.config.enablePageLoadTime) {
      return;
    }
    PageInfo? pageInfo;
    if (_pageStack.isNotEmpty) {
      pageInfo = _pageStack.last;
    }
    developer.log(
        '[PageNavigatorObserver][didReplace]$type ' +
            '_pageStack[${_pageStack.length}]: $_pageStack',
        name: debugLogType.pagePerformance);

    if (pageInfo != _currentPageInfo) {
      _handlePageChanged(pageInfo, _currentPageInfo ?? pageInfo, type);
      _currentPageInfo = pageInfo;
    } else {}
  }

  void _handlePageChanged(
    PageInfo? currentPage,
    PageInfo? previousPage,
    NavigatorType operation,
  ) {
    if (!_pageStack.contains(_loadingPage)) {
      _loadingPage = null;
    }

    if (operation == NavigatorType.push || operation == NavigatorType.replace) {
      _startTime = DateTime.now().millisecondsSinceEpoch;
      _updateTime = 0;
      _endTime = 0;
      _curLoadArea = Rect.zero;
      _isLoaded = false;
      _loadingPage = currentPage;
      _hasReport = false;
    }
  }

  void _onPostFrameCallback(Duration timeStamp) {
    if (!_isLoaded) {
      _updateTime = DateTime.now().millisecondsSinceEpoch;
      _rootView ??= RendererBinding.instance?.renderView;

      _curLoadArea = Rect.zero;
      _rootView?.visitChildren(_visitChild);

      _checkLessWidgetLoaded();
    }
    SchedulerBinding.instance?.addPostFrameCallback(_onPostFrameCallback);
  }

  /// 用于处理界面元素面积垂直区域小于指定阈值比例（默认0.5）的情况
  void _checkLessWidgetLoaded() {
    if (_pageStatus != null && _pageStatus == PageStatus.loading) {
      return;
    }
    if (!_isLoaded && _updateTime > 0 && _loadingPage != null) {
      _isLoaded = true;
      _endTime = _updateTime;
      developer.log(
        '[PageNavigatorObserver][checkLessWidgetLoaded]page time record',
        name: debugLogType.pagePerformance,
      );
      curPageLoadTime = _pageLoadTime();
      _refreshReportState();
    }
  }

  void _refreshReportState() {
    if (!_hasReport &&
        _loadingPage != null &&
        !_loadingPage!.isUnknown() &&
        _isLoaded) {
      _hasReport = true;
    }
  }

  int _pageLoadTime() {
    if (_isLoaded) {
      int result = _endTime - _startTime;
      curPageLoadTime = result;
      if (!_hasReport) {
        developer.log(
          '[PageNavigatorObserver][_pageLoadTime]' +
              'report page:${_currentPageInfo?.pageName}, load time:${result}ms',
          name: debugLogType.pagePerformance,
        );
        _aegis?.send(
          options: Options(
            url:
                '${_aegis?.config.performanceUrl}?from=${_currentPageInfo?.pageName}&referer=${_currentPageInfo?.pageName}&firstScreenTiming=$result&dnsLookup=-1&tcp=-1&ssl=-1&ttfb=-1&contentDownload=-1&domParse=-1&resourceDownload=-1',
            method: Method.GET,
            type: SendType.performance,
          ),
        );
      }

      return result;
    }

    return 0;
  }

  void _visitChild(RenderObject child) {
    if (_pageStatus != null && _pageStatus == PageStatus.loading) {
      return;
    }
    _checkProgress(child);

    // 还未满足覆盖率的要求
    // 没有结果
    if (!_isLoaded) {
      if (child is PageRenderObject) {
        var route = ModalRoute.of(child.element);
        if (route == null) {
          return;
        }
        var pageInfo = PageInfo.route(route);
        if (pageInfo == _loadingPage) {
          developer.log(
              '[PageNavigatorObserver][_pageLoadTime]'
              'pageLoadTime:pageRoot=${child.element.widget}',
              name: debugLogType.pagePerformance);
          _currentPageInfo?.pageWidget = child.element.widget;
          child.element.visitChildren(_findRootWidget);
        }
      }
      child.visitChildren(_visitChild);
    }
  }

  void _checkProgress(RenderObject object) {
    var validType = _validRenderType(object);
    var barHeight = 76;

    if (validType && object.attached) {
      var box = object as RenderBox;
      if (!box.hasSize) {
        developer.log(
          '[PageNavigatorObserver][_checkProgress]'
          'RenderBox was not laid out: $box NEEDS-LAYOUT NEEDS-PAINT',
          name: debugLogType.pagePerformance,
        );

        return;
      }
      var size = box.size;
      var offset = box.localToGlobal(Offset.zero);

      var rect = Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height);

      // 过滤掉标题栏的元素
      if (rect.top + rect.height > barHeight && _rootView != null) {
        _curLoadArea = _curLoadArea == Rect.zero
            ? rect
            : _curLoadArea.expandToInclude(rect);

        var loadVProgress =
            _curLoadArea.height / (_rootView!.size.height - barHeight);
        var loadHProgress = _curLoadArea.width / _rootView!.size.width;
        _isLoaded = (loadVProgress >= _verticalCoverFactor) &&
            (loadHProgress >= _horizontalCoverFactor);

        if (_isLoaded) {
          _endTime = DateTime.now().millisecondsSinceEpoch;
          curPageLoadTime = _pageLoadTime();
          _refreshReportState();
        }
      }
    }
  }

  void _findRootWidget(Element child) {
    var widgetTypeName = child.widget.runtimeType.toString();
    if (PageInfoUtil.blockWidgetType.contains(widgetTypeName)) {
      return;
    }
    _currentPageInfo?.pageName = child.widget.runtimeType.toString();
    developer.log(
      'rootWidget = ${child.widget.runtimeType}',
      name: debugLogType.pagePerformance,
    );
  }

  bool _validRenderType(RenderObject object) {
    // 主要计算
    // UIImage，UILabel，UIButton，UITextView，Switch，Checkbox
    // 这些组件的屏幕覆盖率
    return object is RenderParagraph ||
        object is RenderImage ||
        object.runtimeType.toString() == "_RenderSwitch" ||
        object.runtimeType.toString() == "_RenderCheckbox";
  }

  /// 现在 PV 都算在了自定义事件内
  void _reportPV(String pageName) {
    _aegis?.reportEvent(
      '[PagePV]pageName: $pageName',
    );
  }
}
